#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "Parser.h"
  #include <iostream>

Parser::Parser() {
	this->salida = NULL;
	this->path_terminos = "";
	this->cantidad_doc = 0;
	this->pos_parseada = 0;
	this->tamanio_a_levantar = 0;
}

Parser::~Parser() {
}

void Parser::ejecutarParser(const std::string &directorio, const std::string &path_salida) {
	this->path_terminos = path_salida;
	this->path_terminos.append(SALIDA_PARSER);
	this->salida = fopen(this->path_terminos.c_str(), "w+");
	int id_doc = 0;
	RutasDocumentos rutas(directorio);
	std::string* rutaArchivo = rutas.readNext();
	while(rutaArchivo != NULL){
		this->parsear(*rutaArchivo, id_doc+1);
		delete(rutaArchivo);
		rutaArchivo = rutas.readNext();
		id_doc++;
	}
	fclose(this->salida);
	this->salida = 0;
}

void Parser::parsear(const std::string &path_entrada, unsigned short int doc) {
	bool termino_actual = false;
	bool fin = false;
	char actual;
	int ascii_actual;
	FILE* input= fopen(path_entrada.c_str(), "r");
	fseek(input, 0L, SEEK_END );
	int final = ftell(input);
	char* buffer = NULL;
	int cantidad_parseadas = 0;

	Termino_Parseado termino;
	memset(&termino,0,sizeof(termino));

	if (final < TAM_BUFFER_PRINCIAL) {
		buffer = new char[final];
		memset(buffer,0,final);
	}

	else {
		//todo
	}


	fseek(input,0L, SEEK_SET);
	fread(buffer,sizeof(char),final,input);
	int i = 0;
	char* aux = 0;
	int longitud = 0;

	char* buffer_secundario = new char[TAM_BUFFER_SECUNDARIO];
	int pos_buffer_secundario = 0;



	while (!fin) {
		actual = buffer[i];
		ascii_actual = (int) actual;
		if (((ascii_actual >= 64 && ascii_actual <=90)) || ((ascii_actual >=97) && (ascii_actual <= 122)) || ((ascii_actual>=48) && (ascii_actual <=57))) {
			if (termino_actual) {
				longitud++;
			}
			else {
				termino_actual = true;
				longitud = 1;
				aux = &buffer[i];
			}
			if (((ascii_actual >= 64 && ascii_actual <=90))) {
			    	 buffer[i] = char (tolower(buffer[i]));
			   }
		}
		else {
			if (termino_actual) {
				termino_actual = false;
				if ((sizeof(Termino_Parseado) + pos_buffer_secundario) > TAM_BUFFER_SECUNDARIO) {
					fwrite(buffer_secundario,pos_buffer_secundario,sizeof(char),  this->salida);
					memset(buffer_secundario,0,TAM_BUFFER_SECUNDARIO);
					pos_buffer_secundario = 0;
				}
				termino.doc = doc;
				memcpy(&(termino.termino), &(*aux),longitud);
				memcpy(&buffer_secundario[pos_buffer_secundario], &termino, sizeof(Termino_Parseado));
				pos_buffer_secundario = pos_buffer_secundario + sizeof(Termino_Parseado);
				memset(&termino,0,sizeof(Termino_Parseado));
				cantidad_parseadas++;
			}
		}
		i++;
		if ( i == final) {
			if (termino_actual) {
				if ((longitud + 4 + 4 + pos_buffer_secundario) > TAM_BUFFER_SECUNDARIO) {
					fwrite(buffer_secundario,sizeof(char), pos_buffer_secundario, this->salida);
					memset(buffer_secundario,0,TAM_BUFFER_SECUNDARIO);
					pos_buffer_secundario = 0;
				}
				termino.doc = doc;
				memcpy(&(termino.termino), &(*aux),longitud);
				memcpy(&buffer_secundario[pos_buffer_secundario], &termino, sizeof(Termino_Parseado));
				pos_buffer_secundario = pos_buffer_secundario + sizeof(Termino_Parseado);
				cantidad_parseadas++;
				memset(&termino,0,sizeof(Termino_Parseado));
			}
			fwrite(buffer_secundario,sizeof(char), pos_buffer_secundario, this->salida);
			fin = true;
		}
	}
	delete[] buffer;
	delete[] buffer_secundario;
	fclose(input);
}

bool Parser::levantarTerminos(const std::string &path_entrada, int* size, Termino_Parseado* &terminos_parseados) {

	//Me fijo si hay algun archivo en procesamiento
	if ( this->tamanio_a_levantar == 0) {
		//Sino hay abro el archivo de path de entrada
		FILE* input = fopen(path_entrada.c_str(), "r");
		fseek(input, 0L, SEEK_END );
		//Leo el tamanio total del mismo
		tamanio_a_levantar = ftell(input);
		fclose(input);
	}

	//Calculo los bytes restantes que falta leer del archivo de entrada
	int bytes_restantes = tamanio_a_levantar - pos_parseada;
	if (bytes_restantes == 0) {
		//Si ya se leyeron todos borro el archivo de entrada
		//Pongo los contadores en 0, estado actual ningun archivo en procesamiento
		remove(path_entrada.c_str());
		this->tamanio_a_levantar = 0;
		this->pos_parseada = 0;
		//Ya se leyeron todos los bytes del archivo devuelvo true
		return true;
	}

	else {
		//Me fijo si los bytes a leer son menos que el tamaño del buffer disponible
		if (bytes_restantes < BUFFER_LEVANTAR) {
			//Levanto todos los bytes restantes
			terminos_parseados = this->levantar(path_entrada, size, pos_parseada, pos_parseada + bytes_restantes);
			this->pos_parseada = tamanio_a_levantar;
		}

		else {
			//Si quedan mas que los que tolera el buffer, levanto del archivo
			//un buffer del tamaño maximo posible
			terminos_parseados = this->levantar(path_entrada, size, pos_parseada, pos_parseada + BUFFER_LEVANTAR);
			this->pos_parseada = BUFFER_LEVANTAR + pos_parseada;

		}
		//Todavia no se termino de procesar el archivo
		return false;
	}
}

Termino_Parseado*  Parser::levantar(const std::string &path_entrada, int* size, int byte_desde, int byte_hasta) {

	Termino_Parseado termino;
	memset(&termino, 0 ,sizeof(Termino_Parseado));
	FILE* input = fopen(path_entrada.c_str(), "r");

	int tamanio_buffer  = byte_hasta -byte_desde;

	//Me posiciono en el byte desde donde debo leer
	fseek(input,0L, byte_desde);

	bool salida = false;
	int contador = 0;
	char* buffer = new char[tamanio_buffer];
	memset(buffer,0, (tamanio_buffer));
	char* aux = buffer;

	//Leo la cantidad solicitada de bytes del archivo
	fread(buffer,tamanio_buffer,sizeof(char),input);

	//Calculo a cuantos Terminos_Parseados equivale los bytes pedidos y
	//creo el array de terminos parseados
	int cantidad = tamanio_buffer / sizeof(Termino_Parseado);
	(*size) = cantidad;

	Termino_Parseado* terminos = new Termino_Parseado[cantidad];
	int i = 0;

	while (!salida) {
		/*
		 * Recorro leyendo los terminos parseados hasta
		 * que se leen los bytes pedidos
		 */
		memcpy(&termino,aux,sizeof(Termino_Parseado));

		aux = aux + sizeof(Termino_Parseado);
		contador = contador + sizeof(Termino_Parseado);
		if (contador >= tamanio_buffer) {
			salida = true;
		}
		terminos[i] = termino;
		memset(&termino,0,sizeof(Termino_Parseado));
		i++;
	}
	fclose(input);
	delete[] buffer;
	//Devuelvo el array de terminos parseados
	return terminos;
}

std::string Parser::getPathTerminos() {
	return this->path_terminos;
}
